package com.wiscamp.ninechapters.problems.domain.services;

import com.wiscamp.ninechapters.problems.domain.ProblemsErrorCodes;
import com.wiscamp.ninechapters.problems.domain.gateway.TagGateway;
import com.wiscamp.ninechapters.problems.domain.models.collections.ProblemCollection;
import com.wiscamp.ninechapters.problems.domain.repositories.ProblemCollectionRepository;
import cube.common.cache.CacheManagerNames;
import cube.ddd.aspect.HandleServiceException;
import cube.ddd.domain.BaseDomainService;
import cube.ddd.exception.ExceptionUtils;
import jakarta.validation.constraints.Min;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.CachePut;
import org.springframework.stereotype.Service;

import java.util.Objects;

@Service
public class ProblemCollectionService extends BaseDomainService {

    private final ProblemCollectionRepository problemCollectionRepository;

    private final TagGateway tagGateway;

    private final String CACHE_NAME = "problemCollections";

    public ProblemCollectionService(ProblemCollectionRepository problemCollectionRepository, TagGateway tagGateway) {
        this.problemCollectionRepository = problemCollectionRepository;
        this.tagGateway = tagGateway;
    }


    // region problem collection
    @HandleServiceException
    public ProblemCollection getProblemCollection(long collectionId) {
        var collection = problemCollectionRepository.getProblemCollection(collectionId);
        if (Objects.isNull(collection)) {
            ExceptionUtils.throwServiceException(ProblemsErrorCodes.PROBLEM_COLLECTION_NOT_FOUND, collectionId);
        }
        return collection;
    }

    /*@HandleServiceException
    @Cacheable(value = CACHE_NAME, key = "#collectionId", cacheManager = CacheManagerNames.SIXTY_MINUTES_CACHE)
    public Collection<Long> getProblemIdsByCollection(long collectionId) {
        return problemCollectionRepository.getProblemIdsByCollection(collectionId);
    }

    public long getExamIdByCollection(@Min(0) long collectionId) {
        var collection = getProblemCollection(collectionId);
        if (Objects.nonNull(collection))
            return collection.getExamId();
        return 0;
    }*/


    /*@HandleServiceException
    public ProblemCollection getProblemCollectionByCode(@NotBlank String code, @Min(0) long organizationId) {
        return problemCollectionRepository.getProblemCollectionByCode(code, organizationId);
    }

    @HandleServiceException
    public Collection<ProblemCollection> getProblemCollectionsByName(@NotBlank String name, @Min(0) long organizationId) {
        return problemCollectionRepository.getProblemCollectionsByName(name, organizationId);
    }

    @HandleServiceException
    public Collection<ProblemCollection> getProblemCollectionsByNameContains(@NotBlank String name, @Min(0) long organizationId) {
        return problemCollectionRepository.getProblemCollectionsByNameContains(name, organizationId);
    }

    @HandleServiceException
    public Collection<ProblemCollection> getProblemCollectionsByNameContains(@NotBlank String name, @Min(0) long organizationId, String gradeLevel) {
        var collections = getProblemCollectionsByNameContains(name, organizationId);
        return collections.stream().filter(c->c.getCollectionName().contains(gradeLevel)).collect(Collectors.toList());
    }

    @HandleServiceException
    public Collection<ProblemCollection> getProblemCollectionsByOrganization(@Min(0) long organizationId) {
        return problemCollectionRepository.getProblemCollectionsByOrganization(organizationId);
    }


    @HandleServiceException
    public PagedObjects<ProblemCollection> getProblemCollectionsByQuery(@NotNull PagedQuery query) {
        return problemCollectionRepository.getProblemCollectionsByQuery(query);
    }*/

    /**
     * Creates a item pool
     *
     * @param collection
     * @return
     */
    @CachePut(value = CACHE_NAME, key = "#collection.collectionId", cacheManager = CacheManagerNames.SIXTY_MINUTES_CACHE)
    @HandleServiceException
    public ProblemCollection createProblemCollection(@NotNull ProblemCollection collection, long operatorId) {
        var existedCollection = problemCollectionRepository.getProblemCollectionByCode(collection.getCollectionCode(), collection.getOrganizationId());
        if (Objects.nonNull(existedCollection)) {
            ExceptionUtils.throwServiceException(ProblemsErrorCodes.PROBLEM_COLLECTION_CODE_EXISTS, collection);
        }
        collection.createBy(operatorId);
        return problemCollectionRepository.saveProblemCollection(collection);
    }

    /**
     * Updates a item pool
     *
     * @param collection
     * @return
     */

    @CachePut(value = CACHE_NAME, key = "#collection.collectionId", cacheManager = CacheManagerNames.SIXTY_MINUTES_CACHE)
    @HandleServiceException
    public ProblemCollection updateProblemCollection(@NotNull ProblemCollection collection, long operatorId) {
        var existedCollection = getProblemCollection(collection.getCollectionId());
        existedCollection = problemCollectionRepository.getProblemCollectionByCode(collection.getCollectionCode(), collection.getOrganizationId());
        if (Objects.nonNull(existedCollection) && existedCollection.getCollectionId() != collection.getCollectionId()
                && existedCollection.getCollectionCode().equalsIgnoreCase(collection.getCollectionCode())) {
            ExceptionUtils.throwServiceException(ProblemsErrorCodes.PROBLEM_COLLECTION_CODE_EXISTS, collection);
        }
        collection.updateBy(operatorId);
        return problemCollectionRepository.saveProblemCollection(collection);
    }

    /**
     * Deletes a item pool
     *
     * @param id
     * @return
     */

    @CacheEvict(value = CACHE_NAME, key = "#id")
    @HandleServiceException
    public boolean deleteProblemCollection(@Min(0) long id, long operatorId) {
        var collection = getProblemCollection(id);
        if (Objects.isNull(collection)) return false;
        // 记录操作日志
        collection.delete();
        collection.updateBy(operatorId);
        problemCollectionRepository.saveProblemCollection(collection);
        return true;
    }

    @CacheEvict(value = CACHE_NAME, key = "#id")
    @HandleServiceException
    public boolean enableProblemCollection(@Min(0) long id, long operatorId) {
        var collection = getProblemCollection(id);
        if (Objects.isNull(collection)) return false;
        // 记录操作日志
        collection.enable();
        collection.updateBy(operatorId);
        problemCollectionRepository.saveProblemCollection(collection);
        return true;
    }

    @CacheEvict(value = CACHE_NAME, key = "#id")
    @HandleServiceException
    public boolean disableProblemCollection(@Min(0) long id, long operatorId) {
        var collection = getProblemCollection(id);
        if (Objects.isNull(collection)) return false;
        // 记录操作日志
        collection.disable();
        collection.updateBy(operatorId);
        problemCollectionRepository.saveProblemCollection(collection);
        return true;
    }
    // endregion

    // region collectionProblem
    /*@HandleServiceException
    public CollectionProblem getCollectionProblem(@Min(0) long id) {
        return problemCollectionRepository.getCollectionProblem(id);
    }

    @HandleServiceException
    public CollectionProblem getCollectionProblemByProblem(@Min(0) long collectionId, @Min(0) long problemId) {
        return problemCollectionRepository.getCollectionProblemByProblem(collectionId, problemId);
    }


    @HandleServiceException
    public CollectionProblem getCollectionProblemByProblem(@Min(0) long problemId) {
        return problemCollectionRepository.getCollectionProblemByProblem(problemId);
    }

    @HandleServiceException
    public Collection<CollectionProblem> getCollectionProblems(@Min(0) long collectionId) {
        return problemCollectionRepository.getCollectionProblems(collectionId);
    }*/


    /*@HandleServiceException
    @CacheEvict(value = CACHE_NAME, key = "#collectionId")
    public CollectionProblem addProblem(@Min(0) long collectionId, @Min(0) long partId, @Min(0) long sectionId, @Min(0) long problemId, long operatorId) {
        var existedCollectionProblem = problemCollectionRepository.getCollectionProblemByProblem(problemId);
        if (Objects.nonNull(existedCollectionProblem)) {
            ExceptionUtils.throwServiceException(ProblemsErrorCode.COLLECTION_PROBLEM_EXISTS, problemId);
        }

        var collectionProblem = new CollectionProblem();
        collectionProblem.setCollectionId(collectionId);
        collectionProblem.setProblemId(problemId);
        collectionProblem.setProblemIndex(problemCollectionRepository.getMaxProblemIndex(collectionId) + 1);
        collectionProblem.setPartId(partId);
        collectionProblem.setSectionId(sectionId);

        return problemCollectionRepository.saveCollectionProblem(collectionProblem);
    }*/

    @HandleServiceException
    @CacheEvict(value = CACHE_NAME, key = "#collectionId")
    public boolean removeProblem(@Min(0) long collectionId, @Min(0) long problemId, long operatorId) {
        var existedCollectionProblem = problemCollectionRepository.getCollectionProblemByProblem(collectionId, problemId);
        if (Objects.nonNull(existedCollectionProblem)) {
            // resort problem index
            int currentIndex = existedCollectionProblem.getProblemIndex();
            problemCollectionRepository.resortProblemIndex(collectionId, currentIndex);
            // remove problem from collection
            problemCollectionRepository.deleteCollectionProblem(existedCollectionProblem.getId());
        }
        return true;
    }

    /**
     * Updates the quantity of a item pool
     *
     * @param collectionId
     */
    @HandleServiceException
    public boolean updateProblemQuantity(@Min(0) long collectionId) {
        return problemCollectionRepository.updateProblemQuantity(collectionId);
    }

    /*@HandleServiceException
    public int getMaxProblemIndex(@Min(0) long collectionId) {
        return problemCollectionRepository.getMaxProblemIndex(collectionId);
    }*/

    /*@HandleServiceException
    public long getProblemIdByIndex(@Min(0) long collectionId, @Min(0) int index) {
        return problemCollectionRepository.getProblemIdByIndex(collectionId, index);
    }*/

    @HandleServiceException
    public long getNextProblemId(@Min(0) long collectionId, @Min(0) long problemId) {
        var collectionProblems = problemCollectionRepository.getCollectionProblems(collectionId);

        boolean matchedProblem = false;
        for (var collectionProblem : collectionProblems) {
            long currentProblemId = collectionProblem.getProblemId();
            if (!matchedProblem) {
                if (currentProblemId == problemId) {
                    // 找到当前题，再循环一次获取下一道题编号
                    matchedProblem = true;
                    continue;
                }
            } else {
                return currentProblemId;
            }
        }
        return 0;
    }
    // endregion


    // region batch tag & untag
    @HandleServiceException
    public boolean batchTagProblems(@Min(0) long collectionId, @NotBlank String tagGroupName, @NotBlank String tagName) {
        var problemIds = problemCollectionRepository.getProblemIdsByCollection(collectionId);
        return tagGateway.batchTag(tagGroupName, tagName, problemIds);
    }


    @HandleServiceException
    public boolean batchUntagProblems(@Min(0) long collectionId, @NotBlank String tagGroupName, @NotBlank String tagName) {
        var problemIds = problemCollectionRepository.getProblemIdsByCollection(collectionId);
        return tagGateway.batchUntag(tagGroupName, tagName, problemIds);
    }
    // endregion

}
